package com.atxiaodei.nio.nioday01;

import org.junit.Test;

import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * @author wangmeng
 * @date 2020/9/13
 * 一、Java Stream管道数据处理操作
 * 在本号之前写过的文章中，曾经给大家介绍过 Java Stream管道流是用于简化集合类元素处理的java API。在使用的过程中分为三个阶段。
 * 在开始本文之前，我觉得仍然需要给一些新朋友介绍一下这三个阶段
 * 第一阶段(图中蓝色)：将集合、数组、或行文本文件转换为java Stream管道流
 * 第二阶段(图中虚线部分)：管道流式数据处理操作，处理管道中的每一个元素。上一个管道中的输出元素作为下一个管道的输入元素。
 * 第三阶段(图中绿色)：管道流结果处理操作，也就是本文的将介绍的核心内容。
 **/
public class StreamApi11终端操作 {

    @Test
    public void test1(){
        List<String> nameStrs = Arrays.asList("Monkey", "Lion", "Giraffe","Lemur");

        List<String> list = nameStrs.stream()
                .filter(s -> s.startsWith("L"))
                .map(String::toUpperCase)
                .sorted()
                .collect(Collectors.toList());
        System.out.println(list);
        /**
         * 首先使用stream()方法将字符串List转换为管道流Stream
         * 然后进行管道数据处理操作，先用fliter函数过滤所有大写L开头的字符串，然后将管道中的字符串转换为大写字母toUpperCase，
         * 然后调用sorted方法排序。这些API的用法在本号之前的文章有介绍过。其中还使用到了lambda表达式和函数引用。
         * 最后使用collect函数进行结果处理，将java Stream管道流转换为List。最终list的输出结果是:[LEMUR, LION]
         */
    }

    /**
     * ForEach和ForEachOrdered区别和用法
     * parallel()函数表示对管道中的元素进行并行处理，而不是串行处理，这样处理速度更快。
     * 但是这样就有可能导致管道流中后面的元素先处理，前面的元素后处理，也就是元素的顺序无法保证
     * forEachOrdered从名字上看就可以理解，虽然在数据处理顺序上可能无法保障，
     * 但是forEachOrdered方法可以在元素输出的顺序上保证与元素进入管道流的顺序一致。也就是下面的样子（forEach方法则无法保证这个顺序）：
     */
    @Test
    public void test2(){
        Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
                .parallel()
                .forEach(System.out::println);
        Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
                .parallel()
                .forEachOrdered(System.out::println);
    }


    /**
     * 元素的收集collect
     */
    @Test
    public void test3(){
        // 收集为Set
        Set<String> collect = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
                .collect(Collectors.toSet());
        System.out.println(collect);// 最终collectToSet 中的元素是:[Monkey, Lion, Giraffe, Lemur]，注意Set会去重。

        // 收集到List
        List<String> collect1 = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
                .collect(Collectors.toList());

        // 通用的收集方法
        ArrayList<String> collect2 = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
                .collect(Collectors.toCollection(ArrayList::new));


        LinkedList<String> collect3 = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
                .collect(Collectors.toCollection(LinkedList::new));
        /**
         * 代码中使用了LinkedList::new，实际是调用LinkedList的构造函数，将元素收集到Linked List。
         * 当然你还可以使用诸如LinkedHashSet::new和PriorityQueue::new将数据元素收集为其他的集合类型，这样就比较通用了。
         */

        // 收集到Array
        //通过toArray(String[]::new)方法收集Stream的处理结果，将所有元素收集到字符串数组中。
        String[] strings = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
                .toArray(String[]::new);


    }
    /**
     *  收集到Map
     * 使用Collectors.toMap()方法将数据元素收集到Map里面，但是出现一个问题：那就是管道中的元素是作为key，还是作为value。
     * 我们用到了一个Function.identity()方法，该方法很简单就是返回一个“ t -> t ”（输入就是输出的lambda表达式）。
     * 另外使用管道流处理函数distinct()来确保Map键值的唯一性
     */
    @Test
    public void test4(){
        Map<String, Integer> collect = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
                .distinct() // 去重因为map里面不能有相同重复的键
                .collect(Collectors.toMap(x -> x, y -> y.length()));
        System.out.println(collect);// 输出 {Monkey=6, Lion=4, Lemur=5, Giraffe=7}
        // 用Function.identity() 实现，实际意义就是 Function.identity()==x -> x
        Map<String, Integer> collect1 = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
                .distinct()
                .collect(Collectors.toMap(Function.identity(),// identity()方法代表的就是输入就是输出
                        s -> (int) s.chars().distinct().count()// 输入元素的不同的字母个数，作为value
                ));
        System.out.println(collect1);// 输出{Monkey=6, Lion=4, Lemur=5, Giraffe=6}
    }


    /**
     * 分组收集groupingBy
     * Collectors.groupingBy用来实现元素的分组收集，下面的代码演示如何根据首字母将不同的数据元素收集到不同的List，并封装为Map。
     */
    @Test
    public void test5(){
        Map<Character, List<String>> collect = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
                .collect(Collectors.groupingBy(x -> x.charAt(0)));// 按照首字母去收集到map集合
        System.out.println(collect);
        // 输出 {G=[Giraffe], L=[Lion, Lemur, Lion], M=[Monkey]}

        Map<Character, Long> collect1 = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
                .collect(Collectors.groupingBy(x -> x.charAt(0), Collectors.counting()));// // 加上counting代码可以实现分组统计
        System.out.println(collect1);// 输出{G=1, L=3, M=1}
    }

    /**
     * Stream   其他常用方法
     */
    @Test
    public void test6(){
        boolean b = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
                .anyMatch(new Predicate<String>() {
                    @Override
                    public boolean test(String s) {
                        return "Lion".equals(s);
                    }
                });
        System.out.println(b); // 输出true、

        //上述简写
        boolean b1 = Stream.of(1, 2, 3, 5, 6, 7)
                .anyMatch(s -> 1 == s);
        System.out.println(b1); //输出 true
        // 综上所述  anyMatch方法是判断一个管道有没有包含关系，应用于集合很方便判断

        int sum = IntStream.of(1, 2, 3).sum();
        // 管道中元素数据累加结果sum: 6


        OptionalDouble average = IntStream.of(1, 2, 3).average();
        //管道中元素数据平均值average: OptionalDouble[2.0]



        int max = IntStream.of(1, 2, 3).max().orElse(0);
        //管道中元素数据最大值max: 3



        IntSummaryStatistics statistics = IntStream.of(1, 2, 3).summaryStatistics();
        // 全面的统计结果statistics: IntSummaryStatistics{count=3, sum=6, min=1, average=2.000000, max=3}




    }

    StreamTestVo e1 = new StreamTestVo(1,23,"M","Rick","Beethovan");
    StreamTestVo e2 = new StreamTestVo(2,13,"F","Martina","Hengis");
    StreamTestVo e3 = new StreamTestVo(3,43,"M","Ricky","Martin");
    StreamTestVo e4 = new StreamTestVo(4,26,"M","Jon","Lowman");
    StreamTestVo e5 = new StreamTestVo(5,19,"F","Cristine","Maria");
    StreamTestVo e6 = new StreamTestVo(6,15,"M","David","Feezor");
    StreamTestVo e7 = new StreamTestVo(7,68,"F","Melissa","Roy");
    StreamTestVo e8 = new StreamTestVo(8,79,"M","Alex","Gussin");
    StreamTestVo e9 = new StreamTestVo(9,15,"F","Neetu","Singh");
    StreamTestVo e10 = new StreamTestVo(10,45,"M","Naveen","Jain");


    List<StreamTestVo> list = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
    /**
     *  Collectors操作方法
     */
    @Test
    public void test7(){

        StreamTestVo streamTestVo2 = list.stream().max((x, y) -> x.getAge().compareTo(y.getAge())).get();
        System.out.println(streamTestVo2);

        StreamTestVo streamTestVo = list.stream().collect(Collectors.maxBy((x, y) -> x.getAge().compareTo(y.getAge()))).get();
        System.out.println(streamTestVo);
        StreamTestVo streamTestVo1 = list.stream().collect(Collectors.minBy((x, y) -> x.getAge().compareTo(y.getAge()))).get();
        System.out.println(streamTestVo1);


        // 年龄求和
        Integer sum = list.stream().collect(Collectors.summingInt(StreamTestVo::getAge));
        System.out.println(sum);
        Double ss = list.stream().collect(Collectors.averagingDouble(StreamTestVo::getAge));
        System.out.println(ss);
        //
        DoubleSummaryStatistics vo = list.stream().collect(Collectors.summarizingDouble(StreamTestVo::getAge));
        System.out.println(vo);// 输出DoubleSummaryStatistics{count=10, sum=346.000000, min=13.000000, average=34.600000, max=79.000000}

        vo.getAverage();
        vo.getCount();
        vo.getMax();
        vo.getMin();
        vo.getSum();
    }

}
